# 使用数组来存储堆
# 元素代表节点值，索引代表在二叉树中的位置，节点指针通过索引映射公式实现
# 例如节点索引i，其左子节点的索引是2i+1，右子节点的索引为2i+2，父节点的索引是(i-1)/2
# 常用于
# 1. 优先队列：出入队时间复杂度均为o(log(n))，建队操作位O(n)
# 2. 堆排序
# 3. 获取最大的k个元素
class MaxHeap(object):
    def __init__(self, nums: list[int]):
        self.max_heap = nums

    def swap(self, i, j):
        self.max_heap[i], self.max_heap[j] = self.max_heap[j], self.max_heap[i]

    def size(self):
        return len(self.max_heap)

    def is_empty(self):
        return self.size() == 0

    def left(self, i):
        return 2 * i + 1

    def right(self, i):
        return 2 * i + 2

    def parent(self, i):
        # 向下整除
        return (i - 1) // 2

    def peek(self):
        return self.max_heap[0]

    # 元素入堆
    # 首先将其添加到堆底，添加后由于val可能大于堆中其他元素，因此需要修复插入节点到根节点的路径上的各个节点即堆化
    # 从入堆节点开始，从底至顶执行堆化，比较插入节点与其父节点的值，插入的值更大将其交换，然后继续执行此操作，从底至顶修复堆中的各个节点，直至越过根节点或遇到无须交换的节点时结束
    def push(self, val):
        self.max_heap.append(val)
        self.shif_up(self.size() - 1)

    def sift_up(self, i):
        while True:
            p = self.parent(i)
            if p < 0 or self.max_heap[i] <= self.max_heap[p]:
                break
            self.swap(i, p)
            i = p

    # 堆顶元素出堆
    # 堆顶元素使二叉树的根节点，如果直接从列表中删除首元素，那么二叉树的所有节点的索引都会发生变化
    # 为了减少元素索引的变动，采用的步骤如下
    # 1. 交换堆顶元素与堆底元素
    # 2. 交换完成后，将堆底从列表中删除
    # 3. 从根节点开始，从顶至底执行堆化
    # 从顶至底堆化是将根节点的值与其两个子节点的值进行交换，将最大的子节点与根节点交换
    def pop(self):
        if self.is_empty():
            raise IndexError("堆为空")
        self.swap(0, self.size() - 1)
        val = self.max_heap.pop()
        self.sift_down(0)
        return val

    def sift_down(self, i):
        while True:
            l, r, ma = self.left(i), self.right(i), i
            if l < self.size() and self.max_heap[l] > self.max_heap[ma]:
                ma = l
            if r < self.size() and self.max_heap[r] > self.max_heap[ma]:
                ma = r
            if ma == i:
                break
            self.swap(i, ma)
            i = ma

if __name__ == '__main__':
    max_heap = MaxHeap([9, 8, 7, 6, 7, 6, 2, 1, 4, 3, 6, 2, 5])
    peek = max_heap.pop()
    print(peek)